package com.atjinggu.jinggumail.ware.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.atjinggu.jinggumail.common.enums.OrderStatusEnum;
import com.atjinggu.jinggumail.common.to.mq.OrderTo;
import com.atjinggu.jinggumail.common.to.mq.StockDetailTo;
import com.atjinggu.jinggumail.common.to.mq.StockLockedTo;
import com.atjinggu.jinggumail.common.utils.R;
import com.atjinggu.jinggumail.common.exception.WareStockException;
import com.atjinggu.jinggumail.ware.entity.WareOrderTaskDetailEntity;
import com.atjinggu.jinggumail.ware.entity.WareOrderTaskEntity;
import com.atjinggu.jinggumail.ware.feign.MemberFeignService;
import com.atjinggu.jinggumail.ware.feign.OrderFeignService;
import com.atjinggu.jinggumail.ware.feign.ProductFeignService;
import com.atjinggu.jinggumail.common.to.SkuHasStockVo;
import com.atjinggu.jinggumail.ware.service.WareOrderTaskDetailService;
import com.atjinggu.jinggumail.ware.service.WareOrderTaskService;
import com.atjinggu.jinggumail.ware.vo.AddressVo;
import com.atjinggu.jinggumail.ware.vo.FareVo;
import com.atjinggu.jinggumail.ware.vo.OrderVo;
import com.atjinggu.jinggumail.ware.vo.WareSkuLockVo;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.rabbitmq.client.Channel;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.Data;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.LongStream;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atjinggu.jinggumail.common.utils.PageUtils;
import com.atjinggu.jinggumail.common.utils.Query;

import com.atjinggu.jinggumail.ware.dao.WareSkuDao;
import com.atjinggu.jinggumail.ware.entity.WareSkuEntity;
import com.atjinggu.jinggumail.ware.service.WareSkuService;
import org.springframework.transaction.annotation.Transactional;


@Service("wmsWareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

    @Autowired
    WareSkuDao wareSkuDao;

    @Autowired
    WareOrderTaskService wareOrderTaskService;

    @Autowired
    WareOrderTaskDetailService wareOrderTaskDetailService;

    @Autowired
    ProductFeignService productFeignService;

    @Autowired
    MemberFeignService memberFeignService;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    OrderFeignService orderFeignService;


    @Transactional
    @Override
    public void releaseStock(StockLockedTo to){
        StockDetailTo detail = to.getDetail();
        Long detailId = detail.getId();
        WareOrderTaskDetailEntity byId = wareOrderTaskDetailService.getById(detailId);
        //查询到
        if(null != byId){
            //根据订单号查询订单状态
            Long id = to.getId();
            WareOrderTaskEntity taskEntity = wareOrderTaskService.getById(id);
            String orderSn = taskEntity.getOrderSn();
            R r = orderFeignService.getOrderByOrderSn(orderSn);
            if(r.getCode() == 0){
                OrderVo data = r.getData(new TypeReference<OrderVo>() {
                });
                if(null == data || data.getStatus() == 4 ){
                    //库存锁定状态
                    if(byId.getLockStatus() == 1) {
                        //被取消-回滚
                        unLockStockLocked(detail.getSkuId(), detail.getWareId(), detail.getSkuNum(), detailId);
                    }
                }
            }else{
                throw new RuntimeException("远程查询订单状态失败！");
            }
        }
    }

    private void unLockStockLocked(Long skuId, Long wareId, Integer skuNum,Long detailId) {
        wareSkuDao.unLockStockLocked(skuId,wareId,skuNum);
        WareOrderTaskDetailEntity entity = new WareOrderTaskDetailEntity();
        entity.setId(detailId);
        entity.setLockStatus(2);
        wareOrderTaskDetailService.updateById(entity);
    }

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                new QueryWrapper<WareSkuEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public void addStock(Long skuId, Long wareId, Integer skuNum) {
        //1、判断如果还没有这个库存记录新增
        List<WareSkuEntity> entities = wareSkuDao.selectList(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));
        if(entities == null || entities.size() == 0){
            WareSkuEntity skuEntity = new WareSkuEntity();
            skuEntity.setSkuId(skuId);
            skuEntity.setStock(skuNum);
            skuEntity.setWareId(wareId);
            skuEntity.setStockLocked(0);
            //TODO 远程查询sku的名字，如果失败，整个事务无需回滚
            //1、自己catch异常
            //TODO 还可以用什么办法让异常出现以后不回滚？高级
            try {
                R info = productFeignService.info(skuId);
                Map<String,Object> data = (Map<String, Object>) info.get("skuInfo");

                if(info.getCode() == 0){
                    skuEntity.setSkuName((String) data.get("skuName"));
                }
            }catch (Exception e){

            }
            wareSkuDao.insert(skuEntity);
        }else{
            wareSkuDao.addStock(skuId,wareId,skuNum);
        }

    }

    @Override
    public List<SkuHasStockVo> getSkusHasStock(List<Long> skuIds) {
        List<WareSkuEntity> wareSkuEntities = this.baseMapper.selectList(new QueryWrapper<WareSkuEntity>().in("sku_id",skuIds));
        List<WareSkuEntity> collect = wareSkuEntities.stream().map(item -> {
            if(item.getStock() == null){
                item.setStock(0);
            }
            if(item.getStockLocked() == null){
                item.setStockLocked(0);
            }
            item.setStock(item.getStock() - item.getStockLocked());
            return item;
        }).collect(Collectors.toMap(WareSkuEntity::getSkuId, a -> a, (o1, o2) -> {
                    Integer x = o1.getStock() + o2.getStock();
                    o1.setStock(x);
                    return o1;
                })).values().stream().collect(Collectors.toList());

        List<SkuHasStockVo> collect1 = collect.stream().map(item -> {
            SkuHasStockVo vo = new SkuHasStockVo();
            vo.setSkuId(item.getSkuId());
            vo.setHasStock(item.getStock() > 0 ? true : false);
            return vo;
        }).collect(Collectors.toList());
        if(skuIds.size() == collect.size()) {
            return collect1;
        }else {
            List<Long> collect2 = collect.stream().map(item -> item.getSkuId()).collect(Collectors.toList());
            List<Long> collect3 = skuIds.stream().filter(i -> !collect2.contains(i)).collect(Collectors.toList());
            List<SkuHasStockVo> collect4 = collect3.stream().map(i -> {
                SkuHasStockVo skuHasStockVo = new SkuHasStockVo();
                skuHasStockVo.setSkuId(i);
                skuHasStockVo.setHasStock(false);
                return skuHasStockVo;
            }).collect(Collectors.toList());
            collect1.addAll(collect4);
            return collect1;
        }

    }

    @Override
    public FareVo getFare(String memberId) {
        //int i = new Random().nextInt(100);
        int i = 10;
        FareVo fareVo = new FareVo();
        fareVo.setFare(new BigDecimal(i+""));
        R info = memberFeignService.info(Long.parseLong(memberId));
        AddressVo address = info.getData("memberReceiveAddress", new TypeReference<AddressVo>() {
        });
        fareVo.setAddress(address);
        fareVo.setFare(new BigDecimal(i+""));
        return fareVo;
    }


    @Override
    public void orderLockStock(WareSkuLockVo vo) {


        String orderSn = vo.getOrderSn();

        //保存工作单
        WareOrderTaskEntity taskEntity = new WareOrderTaskEntity();
        taskEntity.setOrderSn(vo.getOrderSn());
        wareOrderTaskService.save(taskEntity);
        List<WareSkuLockVo.WareOrder> locks = vo.getLocks();
        List<SkuWareHasStock> stocks = locks.stream().map(lock -> {
            SkuWareHasStock stock = new SkuWareHasStock();
            //查询出所有商品的仓库
            List<Long> wareIds = wareSkuDao.getWareIdbySkuId(lock.getSkuId());
            stock.setSkuId(Long.parseLong(lock.getSkuId()));
            stock.setWareid(wareIds);
            stock.setNum(lock.getCount());
            return stock;
        }).collect(Collectors.toList());

        for (SkuWareHasStock stock : stocks) {
            boolean stockLock = true;
            List<Long> wareIds =  stock.getWareid();
            Long skuId = stock.getSkuId();
            if(CollectionUtils.isEmpty(wareIds)){
                throw new WareStockException(stock.getSkuId()+"");
            }
            for (Long wareId : wareIds) {
              int count =   wareSkuDao.lockSkuStock(wareId,skuId,stock.getNum());
              if(count == 1){
                  stockLock = true;
                  //保存库存详情
                  WareOrderTaskDetailEntity taskDetailEntity = new WareOrderTaskDetailEntity(null,skuId,"",
                          stock.getNum(),taskEntity.getId(),wareId,1);
                  wareOrderTaskDetailService.save(taskDetailEntity);
                  StockLockedTo lockedTo = new StockLockedTo();
                  StockDetailTo detailTo = new StockDetailTo();
                  lockedTo.setId(taskEntity.getId());
                  lockedTo.setDetail(detailTo);
                  BeanUtils.copyProperties(taskDetailEntity,detailTo);
                  System.out.println("库存服务---开始延迟库存已锁定");
                  rabbitTemplate.convertAndSend("stock-event-exchange","stock.locked",lockedTo);
                  break;
              }else {
                  //继续尝试下一个仓库
              }

            }

            if(!stockLock){
                throw new WareStockException(stock.getSkuId()+"");
            }
        }

    }

    @Transactional
    @Override
    public void releaseStock(OrderTo to) {
        String orderSn = to.getOrderSn();
        //查一下最新库存状态，防止重复解锁库存
        WareOrderTaskEntity entity = wareOrderTaskService.getOrderByOrderSn(orderSn);
        Long taskId = entity.getId();
        //按照工作单找到所有没有解锁的库存，进行解锁
        List<WareOrderTaskDetailEntity> list = wareOrderTaskDetailService.list(new QueryWrapper<WareOrderTaskDetailEntity>()
                .eq("task_id", taskId).eq("lock_status", 1));
        for (WareOrderTaskDetailEntity detailEntity : list) {
            this.unLockStockLocked(detailEntity.getSkuId(),detailEntity.getWareId(),detailEntity.getSkuNum(),detailEntity.getId());
        }

    }


    @Data
    class SkuWareHasStock{
        private  Long skuId;
        private List<Long> wareid;
        private Integer num;
    }


    public static void main(String[] args) {
        List<BillsNums> billsNumsList = new ArrayList<>();
        BillsNums billsNums = new BillsNums();
        billsNums.setId("1001");
        billsNums.setNums(2);
        billsNums.setSums(100);
        billsNumsList.add(billsNums);

        BillsNums billsNums2 = new BillsNums();
        billsNums2.setId("1001");
        billsNums2.setNums(3);
        billsNums2.setSums(100);
        billsNumsList.add(billsNums2);

        List<BillsNums> result = merge(billsNumsList);
        System.out.println("result:" + JSON.toJSONString(result, true));


        List<WareSkuEntity> wareSkuEntities = new ArrayList<>();
        LongStream.rangeClosed(1,3).forEach(
                item -> {
                    WareSkuEntity wareSkuEntity = new WareSkuEntity();
                    wareSkuEntity.setId(item);
                    wareSkuEntity.setSkuId(item);
                    wareSkuEntity.setStock(Integer.parseInt(item*2 + ""));
                    wareSkuEntity.setStockLocked(Integer.parseInt(item + ""));
                    wareSkuEntities.add(wareSkuEntity);
                }
        );

        WareSkuEntity wareSkuEntity = new WareSkuEntity();
        wareSkuEntity.setId(4l);
        wareSkuEntity.setSkuId(1l);
        wareSkuEntity.setStock(100);
        wareSkuEntity.setStockLocked(20);
        wareSkuEntities.add(wareSkuEntity);

        List<WareSkuEntity> collect = wareSkuEntities.stream().map(item -> {
            if(item.getStock() == null){
                item.setStock(0);
            }
            if(item.getStockLocked() == null){
                item.setStockLocked(0);
            }
            item.setStock(item.getStock() - item.getStockLocked());
                return item;
        }).collect(Collectors.toMap(WareSkuEntity::getSkuId, a -> a, (o1, o2) -> {
            Integer t = o1.getStock() + o2.getStock();
            System.out.println("t *******************"+t);
            o1.setStock(t);
            return o1;
        })).values().stream().collect(Collectors.toList());

        collect.forEach((x) -> {
            System.out.println("x *******************" + x);

        });


    }

    public static List<BillsNums> merge(List<BillsNums> list) {
        List<BillsNums> result = list.stream()
                // 表示id为key， 接着如果有重复的，那么从BillsNums对象o1与o2中筛选出一个，这里选择o1，
                // 并把id重复，需要将nums和sums与o1进行合并的o2, 赋值给o1，最后返回o1
                .collect(Collectors.toMap(BillsNums::getId, a -> a, (o1,o2)-> {
                    o1.setNums(o1.getNums() + o2.getNums());
                    o1.setSums(o1.getSums() + o2.getSums());
                    System.out.println("o1 *****************" + o1);
                    return o1;
                })).values().stream().collect(Collectors.toList());
        return result ;
    }


    static class BillsNums {
        private String id;
        private int nums;
        private int sums;

        public String getId() {
            return id;
        }
        public void setId(String id) {
            this.id = id;
        }
        public int getNums() {
            return nums;
        }
        public void setNums(int nums) {
            this.nums = nums;
        }
        public int getSums() {
            return sums;
        }
        public void setSums(int sums) {
            this.sums = sums;
        }
    }



}